#!/usr/bin/env python
# -*- mode: python; coding: utf-8; -*-
##--------------------------------------------------------------------------##
##
## Copyright (C) 1998-2003 Markus Oberhumer <markus@oberhumer.com>
## Copyright (C) 2003 Mt. Hood Playing Card Co.
## Copyright (C) 2005-2010 Skomoroh <skomoroh@users.sourceforge.net>
## Copyright (C) 2010-2013 Jeremy Austin-Bardo <tjaustinbardo@gmail.com>
##
## This program is free software: you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation, either version 3 of the License.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program.  If not, see <http://www.gnu.org/licenses/>.
##
##-- G01 -- S03 ------------------------------------------------------------##
__all__ = []

from .. import pilelib as stack
from ..formlib import Layout
from ..gamelib import info as gi, game, state
from ..hint import CautiousDefaultHint
from ..packlib import place, info as cs
from ..pilelib import move as a_move

GT, SL = gi.GT, gi.SL
StackWrapper = stack.StackWrapper


class Matriarchy_Waste(stack.WasteStack):
    """
    Build none. Play top. Force update on _talon_ text when max_rounds change.
    """

    @state.not_state('is_preview')
    def _update_text(self):
        """
        _update_text() -> None

        Update number of cards on a stack as well as _talon_ when
        _talon_._updateMaxRounds.
        """
        super(Matriarchy_Waste, self)._update_text()
        if self.game.s['talon']._updateMaxRounds():
            self.game.s['talon']._update_text()


class Matriarchy_Talon(stack.WasteTalon):
    """
    Deal vary card on _waste_. Vary cards begin 2 cards and +1 through 11
    turns, then after -1. Each full seq add 1 turn, all of suit add 2 turn.
    """
    DEAL = (2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 11, 10, 9, 8, 7, 6, 5,)

    def _updateMaxRounds(self):
        """
        _updateMaxRounds() -> bool

        Recompute max_rounds.
        """
        old = self.max_rounds
        self.max_rounds = 11
        rows = self.game.s['rows']
        for i, j in zip(range(0, 7, 2), range(1, 8, 2)):
            l1 = len(rows[i].cards) + len(rows[i + 8].cards)
            l2 = len(rows[j].cards) + len(rows[j + 8].cards)
            assert l1 + l2 <= 26
            if l1 + l2 == 26:
                self.max_rounds += 2
            elif l1 >= 13 or l2 >= 13:
                self.max_rounds += 1
        if self.max_rounds == 19:  # game is won
            self.max_rounds = 18
        return old != self.max_rounds

    def can_deal_cards(self):
        """
        can_deal_cards() -> bool

        Check by Game.talon_deal() if pile can deal more cards. Default is
        while there are cards.

        :return: deal more cards?
        :rtype: boolean
        """
        if self._updateMaxRounds():
            self._update_text()
        self.num_cards = self.__DEAL__[self.round - 1]
        return super(Matriarchy_Talon, self).can_deal_cards()

    def deal_cards(self, sound=False):
        """
        deal_cards([sound:bool=False]) -> None

        Deal cards for Game.talon_deal() to other piles. Default is deal
        num_cards and auto reset when gap once a deal.

        :keyword boolean sound: sound fx?
        """
        assert self.numcards > 0
        game = self.game

        def dealWaste(ncards=None):
            if sound and game.can_play_sound:
                game._play_sound("dealwaste")
            ncards = ncards or min(self.cards, self.num_cards)
            for i in range(ncards):
                game._move_do(a_move.AFlipAndMoveMove(self, game.s['wastes'][-1],
                                                      frames=4),
                                reset=True)  # low-level
            return ncards

        ncards = dealWaste()
        if ncards < self.num_cards and game.s['wastes'][-1].cards:
            assert not self.cards and self.round < self.max_rounds
            if sound and game.can_play_sound:
                game._play_sound("turnwaste", priority=20)
            game._move_do(a_move.ATurnStackMove(game.s['wastes'][-1], self),
                          )  # low lovel
            game._move_do(a_move.ANextRoundMove(self), reset=True)  # low level
            ncards += dealWaste(self.num_cards - ncards)
        assert self.round <= self.max_rounds
        assert self.num_cards == ncards or not self.cards
        assert game.s['wastes'][-1].cards
        return ncards

    @state.not_state('is_preview')
    def _update_text(self, **kw):
        """
        _update_text() -> None

        Update number of cards on a stack as well as round/max_rounds and
        num_cards.
        """
        kw.setdefault('update_rounds', False)
        super(Matriarchy_Talon, self)._update_text(**kw)
        if self.texts['rounds']:
            self.texts['rounds'].config(text=_('Round {0.round/0.max_rounds}'
                                               ).format(self))
        if self.texts['misc']:
            self.texts['misc'].config(text=_("Deal {0.num_cards}").format(self))


class RowStack(stack.SS_RowStack):
    """
    Build down in rank by same suit. Play Seq. _BOTTOM_IMAGE_ is 'suit'.
    """
    _BOTTOM_IMAGE_ = 'suit'


class Matriarchy(game.Game):
    """
    Finish cards on _foundation_ within 11+ turn.

    _row_
      Pile 16 over 2 row. Init deal base QUEEN S. Build in rank by same suit:
      N up from base KING wrap. S down from base QUEEN. Play top. Each full
      seq add 1 turn, all of suit add 2 turn.

    _talon_
      Pile 1 W. Deal vary card on _waste_. Vary cards begin 2 cards and
      +1 through 11 turns, then after -1.

    _waste_
      Pile 1 S _talon_. Init deal 1 card. Build none. Play top.

    ------
    :cvar Stack _talon_: Matriarchy_Talon (max_rounds=VARY)
    :cvar Stack _waste_: Matriarchy_Waste
    :cvar Stack _row_: RowStack (base_rank=KING, mod=13, dir=1, min_cards=1,
      max_cards=12), RowStack (base_rank=QUEEN, mod=13, dir=-1, min_cards=1,
      max_cards=12)
    """
    __info__ = dict(id=17, name="Matriarchy", game_type=(GT.TWO_DECK |
                    GT.ATARI), decks=2, redeals=cs.VARY, skill_level=SL.BALANCED,
                    version='4.20')
    __author__ = 'Markus Oberhumer'
    __version__ = 7.70

    _talon_ = StackWrapper(Matriarchy_Talon, max_rounds=cs.VARY)
    _waste_ = Matriarchy_Waste
    _rows_ = (StackWrapper(RowStack, base_rank=cs.KING, mod=13, dir=1,
                           min_cards=1, max_cards=12),
              StackWrapper(RowStack, base_rank=cs.QUEEN, mod=13, dir=-1,
                           min_cards=1, max_cards=12),)
    _hint_ = CautiousDefaultHint

    def _create_game(self, playcards=11, **kw):
        l, s = Layout(self), self.s
        dy = max(2 * l.YS, l._get_stack_height(playcards, l.YS))
        self.table_size = (10 * l.XS + l.XM, l.YM + 2 * dy,)
        center = self.height / 2
        c1, c2 = center - l.TEXT_HEIGHT / 2, center + l.TEXT_HEIGHT / 2
        y = dy - l.YS - l.TEXT_HEIGHT / 2
        for o, _row_ in zip((-1, 1,), self._rows_):
            x = l.XM
            for suit in l.NSUITS:
                s['rows'].append(_row_(x, y, game=self, suit=suit,
                                    offset=(0, l.YOFFSET * o)))
                x += l.XS
            y += l.TEXT_HEIGHT + l.YS
        x, y = x + l.XS / 2, dy - 3 * l.YS / 2
        s['wastes'].append(self._waste_(x, y, game=self))
        l.set_ncards_text(s['wastes'][-1], "s")
        y = self.height - dy + l.YS / 2
        s['talon'] = self._talon_(x, y, game=self)
        l.set_ncards_text(s['talon'], "n")
        l.set_rounds_text(s['talon'], 'ss')
        l.set_misc_text(s['talon'], 'e')
        l.defaultStackGroups()
        return l

    def _pack_place(self, cards):
        """
        _pack_place(cards:seq(Card ...)) -> seq(Card ...)

        Overide to return QUEEN on top by rank, suit, deck for _row_ deal.
        """
        return place.MoveToTop(cards, place.AllQueen)

    def _start(self):
        self._play_sound('deal')
        self.s['talon'].dealRow(self.s['rows'][8:])
        self.s['talon'].deal_cards()

    @property
    def is_complete(self):
        return not any(p.cards for p in (s['talon'], s['wastes'][-1],))

PROVIDES = [Matriarchy]